home *** CD-ROM | disk | FTP | other *** search
/ Amiga Format CD 52 / Amiga Format AFCD52 (Issue 136, May 2000).iso / -in_the_mag- / multitasking / coders / ppipc / ipc_lib_sources / ipclib.c < prev    next >
Text File  |  2000-03-05  |  20KB  |  476 lines

  1. /*******************************************************************
  2.  *                                                                 *
  3.  *                           IPClib.c                              *
  4.  *                                                                 *
  5.  *           Inter-Process-Communication Procedures                *
  6.  *              in Shared Library Form                             *
  7.  *                                                                 *
  8.  *              Release  2.1 -- 1989 March 27                      *
  9.  *                                                                 *
  10.  *              Copyright 1988,1989 Peter Goodeve                  *
  11.  *                                                                 *
  12.  *  This source is freely distributable, but its functionality     *
  13.  *  should not be modified without prior consultation with the     *
  14.  *  author.  [Don't forget this is a SHARED library!]              *
  15.  *                                                                 *
  16.  *******************************************************************/
  17.  
  18. /*******************************************************************
  19.  *                                                                 *
  20.  *  Modification History:                                          *
  21.  *                                                                 *
  22.  *      88:7:22     PutIPCPort accepts IPP_LOADING flag            *
  23.  *                                                                 *
  24.  *      88:7:11     Manx/Aztec compatibility added                 *
  25.  *                  CheckIPCPort now has flag return option        *
  26.  *                  FindIPCPort now fails if no server             *
  27.  *                  CreateIPCMsg has two added args                *
  28.  *                                                                 *
  29.  *      89:3:25     (Lattice 5.0) Shared Resident Library version. *
  30.  *                  Return from CheckIPCPort is defined ULONG      *
  31.  *                  to avoid confusion.                            *
  32.  *                                                                 *
  33.  *      89:3:27     IPP_LOADED flag detection added to             *
  34.  *                  FindIPCPort.                                   *
  35.  *                                                                 *
  36.  *                                                                 *
  37.  *******************************************************************/
  38.  
  39. /********************************************************************
  40.  *                                                                  *
  41.  *  Synopsis of usage:                                              *         *
  42.  *  ========                                                        *
  43.  *                                                                  *
  44.  *  (via library interface stub routines)                           *
  45.  *                                                                  *
  46.  *    Client:                                                       *
  47.  *                                                                  *
  48.  *          port = GetIPCPort(name);                                *
  49.  *      or  port = FindIPCPort(name)                                *
  50.  *          .....                                                   *
  51.  *          msg = CreateIPCMsg(nitems, nxbytes, replyport);         *
  52.  *          .....                                                   *
  53.  *          PutIPCMsg(port,msg);                                    *
  54.  *          .....                                                   *
  55.  *          DeleteIPCMsg(msg);                                      *
  56.  *          .....                                                   *
  57.  *          DropIPCPort(port);                                      *
  58.  *                                                                  *
  59.  *    Server: [standard Exec procedures in brackets]                *
  60.  *                                                                  *
  61.  *          port = ServeIPCPort(name);                              *
  62.  *          .....                                                   *
  63.  *          [WaitPort(port); or Wait(sigbits);]                     *
  64.  *          .....                                                   *
  65.  *          [msg = GetMsg(port);]                                   *
  66.  *          .....                                                   *
  67.  *          [ReplyMsg(msg);]                                        *
  68.  *          .....                                                   *
  69.  *          ShutIPCPort(port);                                      *
  70.  *          <handle remaining messages on the port>                 *
  71.  *          LeaveIPCPort(port);                                     *
  72.  *                                                                  *
  73.  *    Misc.:                                                        *
  74.  *                                                                  *
  75.  *          UseIPCPort(port);                                       *
  76.  *          CheckIPCPort(port,flags);                               *
  77.  *                                                                  *
  78.  *                                                                  *
  79.  ********************************************************************/
  80.  
  81. /********************************************************************
  82.  *                                                                  *
  83.  * These procedures provide a mechanism for independent processes   *
  84.  * to communicate with each other through "IPC ports".   These are  *
  85.  * similar to standard Exec Message Ports, except that they are     *
  86.  * not "owned" by any of the processes; any one process can         *
  87.  * declare itself a "server" (provided no other is currently        *
  88.  * claiming this right), and thus becomes temporarily the handler   *
  89.  * of messages passed to this port.  If there is no server, any     *
  90.  * attempt to send a message to a port will return failure, and     *
  91.  * the client may take whatever action is appropriate.  A client    *
  92.  * may safely "Get" a pointer to a named port (even if there is no  *
  93.  * server yet) and can rely on it remaining valid until it "Drops"  *
  94.  * it again. (In contrast to Exec ports, which have no such         *
  95.  * protection.)                                                     *
  96.  *                                                                  *
  97.  * IPC Ports don't appear in the Exec named port list -- they have  *
  98.  * their own.                                                       *
  99.  *                                                                  *
  100.  *                                                                  *
  101.  * Other modules will doubtless have to be added.  One such is      *
  102.  * "LoadIPCPort(name)" which, after doing a GetIPCPort(), will      *
  103.  * check the returned port to see if it has a  server; if not, it   *
  104.  * will check to see if there is a "broker"  process serving the    *
  105.  * IPCBasePort, and if so will send a message to that asking for    *
  106.  * the port to be "served".  (The broker is essentially a server    *
  107.  * like any other -- it will probably look up the port in a user    *
  108.  * supplied list and load the specified server; more advanced       *
  109.  * models may check to see if the server is on another machine,     *
  110.  * for example.)                                                    *
  111.  *                   - - - - - - - - - - -                          *
  112.  *                                                                  *
  113.  *   This code has only been tested under Lattice 5.02.             *
  114.  *   It MUST be compiled with -b0 -v options                        *
  115.  *   (32 bit addressing & no stack check)                           *
  116.  *   AND linked with lcnb.lib  (NOT standard lc.lib).               *
  117.  *   The "__asm" keyword and associated mechanisms have been used   *
  118.  *   to allow direct passing of parameters in registers (the "-r"   *
  119.  *   switch of the compiler could probably have been used instead). *
  120.  *                                                                  *
  121.  *                   - - - - - - - - - - -                          *
  122.  *                                                                  *
  123.  *                                                                  *
  124.  *      %%  The fundamental concept of keeping a use-count  %%      *
  125.  *      %%  on a port is due to Matt Dillon.  Thanks Matt!  %%      *
  126.  *                                                                  *
  127.  ********************************************************************/
  128.  
  129.  
  130. #include "IPCStruct.h"
  131. /* This is IPC.h + IPCPorts.h - (Flags & Prototypes) */
  132.  
  133. /* As we're restricted to Lattice 5, use direct Exec calls: */
  134. #include <proto/exec.h>
  135.  
  136. #include <exec/types.h>
  137. #include <exec/libraries.h>
  138. #include <exec/memory.h>
  139. #include <exec/tasks.h>
  140.  
  141. #define  SOL(s)  ((LONG)sizeof(s))
  142.  
  143.  
  144. struct List PortList;  /* All IPC Ports are on this list */
  145.  
  146.  
  147.  
  148. /*
  149.  *  CreateIPCPort is a private procedure which should not be called
  150.  *  directly by a user program.  (Actually it is only called by GetIPCPort
  151.  *  but for clarity it is kept separate.)
  152.  *  The created IPCPort will be added to the PortList
  153.  *  (GetIPCPort won't allow duplicate names) unless the name is NULL,
  154.  *  in which case an "anonymous" port will be created that does not get
  155.  *  onto the list; such anonymous ports can only be accessed by other
  156.  *  processes if they are themselves passed as pointers in messages.
  157.  *  (Note that the call to this procedure MUST be within a
  158.  *  Forbid()/Permit() pair.)
  159.  */
  160.  
  161. static struct IPCPort * CreateIPCPort(name) char *name;
  162. {
  163.     struct IPCPort * port;
  164.     int psize;
  165.  
  166.     psize = sizeof(struct IPCPort) + (name ? strlen(name) : 0);
  167.         /* psize is actually one byte too big -- do you care? */
  168.     port = (struct IPCPort *)
  169.            AllocMem((LONG)psize, MEMF_CLEAR | MEMF_PUBLIC);
  170.  
  171.     if (port) {
  172.         port->ipp_Size = (UWORD)psize;
  173.         NewList(&(port->ipp_Port.mp_MsgList));
  174.         port->ipp_Port.mp_Node.ln_Type = NT_MSGPORT;
  175.         port->ipp_Port.mp_Flags = PA_IGNORE; /* initially */
  176.         if (name) { /* anonymous port is not put on list */
  177.           port->ipp_Port.mp_Node.ln_Name = port->ipp_Name;
  178.                     /* point to name storage array */
  179.           strcpy(port->ipp_Name, name); /* move name to permanent storage */
  180.           AddHead(&PortList, (struct Node *)port);
  181.         }
  182.     }
  183.     return port;
  184. }
  185.  
  186.  
  187.  
  188. /*
  189.  *  UseIPCPort
  190.  *
  191.  *     Registers another connection to a port (outside this module,
  192.  *     this procedure is only used  when the port was passed as a
  193.  *     pointer from another process).
  194.  *     (Use DropIPCPort when done.)
  195.  *     If the current server has set the IPP_NOTIFY flag in the port, the
  196.  *     server task will be signalled using the port signal bit.  NOTE that
  197.  *     WaitPort will NOT detect these signals, because no message is
  198.  *     actually sent; the program must do a Wait on this bit, and should do
  199.  *     a GetMsg as usual, but if the message pointer is null it should
  200.  *     then call, for example, CheckIPCPort.
  201.  *     NOTE -- the port pointer MUST remain valid while this procedure is
  202.  *     called: either the call (from e.g. GetIPCPort) is Forbid()den, or
  203.  *     the port was passed from another process which guarantees its
  204.  *     existence.
  205.  */
  206.  
  207. void __asm UseIPCPort(register __a0 struct IPCPort * port)
  208. {
  209.     port->ipp_UseCount++;
  210.     if (port->ipp_Flags & IPP_NOTIFY) /* Server wants to be notified */
  211.         Signal(port->ipp_Port.mp_SigTask,
  212.                1L<<port->ipp_Port.mp_SigBit);
  213. }
  214.  
  215.  
  216.  
  217. /*
  218.  *  FindIPCPort
  219.  *
  220.  *     Finds the IPCPort with the name supplied as argument if
  221.  *     it exists and will accept messages (Server exists or is loading).
  222.  *     Returns pointer to port if it exists AND has a server (maybe loading)
  223.  *      -- null otherwise;
  224.  *     registers a new connection to the port (i.e. increments UseCount)
  225.  *     via UseIPCPort.
  226.  *     (Connection must be terminated when program is done by the procedure
  227.  *     DropIPCPort.)
  228.  *     It will notify a server if requested (see UseIPCPort).
  229.  */
  230.  
  231. struct IPCPort * __asm FindIPCPort(register __a0 char * name)
  232. {
  233.     struct IPCPort * port;
  234.     Forbid();
  235.     port = (struct IPCPort *)FindName(&PortList, name);
  236.     if (port) {
  237.         if (port->ipp_Flags & (IPP_SERVED |IPP_LOADING))
  238.             UseIPCPort(port);
  239.         else
  240.             port = NULL; /* no good if not currently served */
  241.     }
  242.     Permit();
  243.     return port;
  244. }
  245.  
  246.  
  247. /*
  248.  *  GetIPCPort
  249.  *
  250.  *     Returns a pointer to IPCPort with the name supplied as an argument;
  251.  *     unlike FindIPCPort, it always returns pointer to port -- this is
  252.  *     created if it doesn't exist; registers a new connection to the port
  253.  *     (use DropIPCPort when done).  It will notify a server if requested
  254.  *     (see UseIPCPort).
  255.  */
  256.  
  257. struct IPCPort * __asm GetIPCPort(register __a0 char * name)
  258. {
  259.     struct IPCPort * port=NULL;
  260.     Forbid();
  261.     if (name) /* port could be anonymous */
  262.         port = (struct IPCPort *)FindName(&PortList, name);
  263.     if (!port)
  264.         port = CreateIPCPort(name);
  265.     if (port)
  266.         UseIPCPort(port);
  267.     Permit();
  268.     return port;
  269. }
  270.  
  271.  
  272. /*
  273.  *  DropIPCPort
  274.  *
  275.  *     Terminate a connection to a port established by FindIPCPort,
  276.  *     GetIPCPort, or UseIPCPort.  Port will be destroyed if there are
  277.  *     no other connections left.
  278.  *     If the IPP_NOTIFY flag is set in the port, the server will be
  279.  *     signalled when this procedure is called (see FindIPCPort).
  280.  */
  281.  
  282. void __asm DropIPCPort(register __a0 struct IPCPort * port)
  283. {
  284.     if (!port) return; /* to save the client some trouble
  285.                           (in a cleanup procedure) */
  286.     Forbid();
  287.     if (--port->ipp_UseCount == 0) {
  288.         /* an anonymous port is NOT on list -- ALL others MUST be! */
  289.         if (port->ipp_Port.mp_Node.ln_Name) Remove((struct Node *)port);
  290.         Permit();
  291.         FreeMem(port, (ULONG)port->ipp_Size);
  292.     }
  293.     else {
  294.         if (port->ipp_Flags & IPP_NOTIFY) /* Server wants to be notified */
  295.             Signal(port->ipp_Port.mp_SigTask,
  296.                    1L<<port->ipp_Port.mp_SigBit);
  297.         Permit();
  298.     }
  299. }
  300.  
  301.  
  302. /*
  303.  *  ServeIPCPort
  304.  *
  305.  *     Registers calling task as the server on the named port (which is
  306.  *     created if it doesn't exist); null is returned if the port already
  307.  *     has a server, otherwise a pointer to it is returned.  At the same
  308.  *     time the port is given the server as its SigTask and a suitable
  309.  *     signal bit is allocated.
  310.  */
  311.  
  312. struct IPCPort * __asm ServeIPCPort(register __a0 char *name)
  313. {
  314.     struct IPCPort * port;
  315.  
  316.     port = GetIPCPort(name);
  317.     if (port) {
  318.         Forbid();
  319.         if ((port->ipp_Flags & (IPP_SERVED | IPP_SHUT))
  320.          || (port->ipp_Port.mp_SigBit = AllocSignal(-1L)) == -1L) {
  321.             DropIPCPort(port);
  322.             port = NULL;
  323.         }
  324.         else {
  325.             port->ipp_Port.mp_Flags = PA_SIGNAL;
  326.             port->ipp_Port.mp_SigTask = FindTask(NULL);
  327.             port->ipp_Flags = IPP_SERVED; /* all other bits cleared */
  328.         }
  329.         Permit();
  330.     }
  331.     return port;
  332. }
  333.  
  334. /*
  335.  *  ShutIPCPort
  336.  *
  337.  *     ONLY the current server may call this procedure.
  338.  *     It prevents more messages being sent to this port, but
  339.  *     does not end server connection; remaining messages can be dealt
  340.  *     with before finally calling LeaveIPCPort.
  341.  *     Note that it does NOT inhibit the server being signalled if
  342.  *     IPP_NOTIFY is set and another client connects.  (At the moment
  343.  *     there is no mechanism to reopen a shut port without Leaving
  344.  *     first; this may be possible in a future revision.)
  345.  */
  346.  
  347. void __asm ShutIPCPort(register __a0 struct IPCPort * port)
  348. {
  349.     Forbid(); /* now required because of FindIPCPort test */
  350.     port->ipp_Flags |= IPP_SHUT; /* Prevent other servers connecting */
  351.     port->ipp_Flags &= ~IPP_SERVED; /* prevent messages from landing */
  352.     port->ipp_Port.mp_Flags = PA_IGNORE; /* someone might use PutMsg! */
  353.     Permit();
  354. }
  355.  
  356. /*
  357.  *  LeaveIPCPort
  358.  *
  359.  *     ONLY the current server may call this procedure.
  360.  *     Disconnects the server process from the port; another process
  361.  *     can then become a server if it desires.  If there are no other
  362.  *     connections, the port is removed.
  363.  */
  364.  
  365. void __asm LeaveIPCPort(register __a0 struct IPCPort * port)
  366. {
  367.     FreeSignal(port->ipp_Port.mp_SigBit);
  368.     port->ipp_Port.mp_SigTask = NULL;
  369.     port->ipp_Flags &= ~(IPP_SHUT | IPP_SERVED | IPP_SERVER_FLAGS);
  370.     DropIPCPort(port);
  371. }
  372.  
  373.  
  374. /*
  375.  *  CheckIPCPort
  376.  *
  377.  *     Normally returns the number of current connections to this port
  378.  *     (including the server); if the top bit (0x8000) of the flags
  379.  *     argument is set, it will instead return the IPCPort flag word
  380.  *     value at the time of the call.  (Note that these values are
  381.  *     not guaranteed!)
  382.  *     A call by the server (only) will also set the (user
  383.  *     settable) port flags to the value in the second argument --
  384.  *     currently the only valid flag is IPP_NOTIFY.
  385.  */
  386.  
  387. ULONG __asm CheckIPCPort(
  388.        register __a0 struct IPCPort *port,
  389.        register __d0 UWORD flags)
  390. {
  391.     UWORD origflags = port->ipp_Flags;
  392.     if (port->ipp_Port.mp_SigTask == FindTask(NULL))
  393.         /* only server can change flags */
  394.         port->ipp_Flags = (port->ipp_Flags & ~IPP_SERVER_FLAGS) |
  395.                           (flags & IPP_SERVER_FLAGS);
  396.     return (ULONG)((flags & 0x8000)? origflags : port->ipp_UseCount);
  397. }
  398.  
  399.  
  400. /*
  401.  *  PutIPCMsg
  402.  *
  403.  *     Sends an IPCMessage to an IPCPort; if the port has no server or
  404.  *     is shut, the message is not sent and the function returns FALSE;
  405.  *     otherwise it returns TRUE. (Other port flags to be added later
  406.  *     may affect these actions.)
  407.  *     Note that a ReplyPort should be supplied in the message as usual
  408.  *     (except for the rare possible usage where a message is not to
  409.  *     be replied; in this case, the IPC_TRANSFER flag must be set in
  410.  *     both the message and all the items it contains, and the ReplyPort
  411.  *     must be NULL).
  412.  */
  413.  
  414. ULONG __asm PutIPCMsg(
  415.             register __a0 struct IPCPort *port,
  416.             register __a1 struct IPCMessage *msg)
  417. {
  418.     Forbid(); /* we do this the straightforward way -- it's very quick */
  419.     if (port->ipp_Flags & (IPP_SERVED | IPP_LOADING)) {
  420.         PutMsg((struct MsgPort *)port, (struct Message *)msg);
  421.         Permit();
  422.         return TRUE;
  423.     }
  424.     Permit();
  425.     return FALSE;
  426. }
  427.  
  428.  
  429. /*
  430.  *  CreateIPCMsg
  431.  *
  432.  *     Creates a standard IPCMessage block (in MEMF_PUBLIC) with the
  433.  *     number of IPCItems supplied as argument. (Special cases -- like
  434.  *     in-line data -- you will have to handle yourself, and you always
  435.  *     have to manage the data blocks yourself).
  436.  */
  437.  
  438. struct IPCMessage * __asm CreateIPCMsg(
  439.     register __d0 int nitems,
  440.     register __d1 int nxbytes,
  441.     register __a0 struct MsgPort *replyport)
  442. {
  443.     ULONG msgsize, mlength;
  444.     struct IPCMessage * mp;
  445.     msgsize = sizeof(struct IPCMessage) +
  446.                   (nitems - 1)*SOL(struct IPCItem) + nxbytes;
  447.     mlength = msgsize - sizeof(struct Message);
  448.     if (mlength > 0x0000FFFF)
  449.         return NULL; /* no oversize messages! */
  450.     mp = (struct IPCMessage *)
  451.          AllocMem(msgsize, MEMF_CLEAR | MEMF_PUBLIC);
  452.     if (mp) {
  453.         mp->ipc_Msg.mn_Length = mlength;
  454.         mp->ipc_ItemCount = nitems;
  455.         mp->ipc_Msg.mn_ReplyPort = replyport;
  456.     }
  457.     return  mp;
  458. }
  459.  
  460.  
  461. /*
  462.  *  DeleteIPCMsg
  463.  *
  464.  *     Deletes a standard IPCMessage block;  you must first have disposed
  465.  *     of any attached data as appropriate.
  466.  */
  467.  
  468. void __asm DeleteIPCMsg(register __a0 struct IPCMessage *msg)
  469. {
  470.     FreeMem(msg, SOL(struct Message) + msg->ipc_Msg.mn_Length);
  471. }
  472.  
  473.  
  474.             /*********************************************/
  475.  
  476.